import os
import sys
import queue

from base.build_graph import BuildGraph
from base.build_statistics import BuildStatistics
from base.defines import *
from base.context import Context
from utils.common_utils import run_notty_command


def mpl_file(context, md5_code):
    return os.path.join(context.MAPLE_DIR, md5_code + context.MAPLE_EXT)

def obj_file(context, md5_code):
    return os.path.join(context.OBJ_DIR, md5_code + context.OBJ_EXT)

def assemble_file(context, md5_code):
    return os.path.join(context.ASSEMBLE_DIR, md5_code + context.ASSEMBLE_EXT)

def dy_lib_file(context, md5_code):
    return os.path.join(context.BIN_DIR, md5_code + context.DYLIB_EXT)

def st_lib_file(context, md5_code):
    return os.path.join(context.BIN_DIR, md5_code + context.STLIB_EXT)



class BuildComplete(object):
    def __init__(self, context=None):
        self.build_graph = BuildGraph()
        self.work_queue = queue.Queue()
        self.finished_queue = queue.Queue()
        self.build_statistics = BuildStatistics()
        self.context = context
    
    def auto_replace_IO_option_compile(self, work_node, parser):
        db_handle = self.build_graph.read_attribute(work_node, Attribute.DB_HANDLER)
        link_dep = db_handle.get_link_dep()
        deps = link_dep.get(work_node)
        output_opt = parser.get_output_options()[0]
        input_opt = parser.get_input_options()[0]
        input_opt.parameter = [mpl_file(self.context, work_node)]
        output_opt.parameter = [obj_file(self.context, work_node)]
        
        return deps
    
    def complete_build(self):
        # to complete ir -> obj -> bin the build process
        reversed_graph = self.build_graph.get_reversed_graph_with_degree()
        root_nodes = self.build_graph.get_root_nodes()
        finished_nodes = [node for node, indegree in reversed_graph.in_degree_iter() if indegree == 0]
        
        for node in finished_nodes:
            self.finished_queue.put(node)
        
        while not self.finished_queue.empty():
            
            self.__update_work_graph(reversed_graph)
            while not self.work_queue.empty():
                work_node =  self.work_queue.get()
                ori_command = self.build_graph.read_attribute(work_node, Attribute.COMMAND)
                parser = self.build_graph.read_attribute(work_node, Attribute.PARSER)(command=ori_command, context=self.context)
                compiler = self.build_graph.read_attribute(work_node, Attribute.COMPILER)
                file_name = self.build_graph.read_attribute(work_node, Attribute.NAME)
                parser.parse_command()
                if compiler == CompileType.GCC or compiler == CompileType.CLANG:
                    deps = self.auto_replace_IO_option_compile(work_node, parser)
                    # print("Compile: %s\tDependencies: %s"% (file_name, ' '.join(deps)))
                    compile_command = parser.get_compile_command()
                    assembly_command = parser.get_assembly_command()
                    ret, std_out, std_err = run_notty_command(compile_command)
                    if ret:
                        print("Compile Failed Command:\n\t%s\nError Message:\n\t%s" % (compile_command, std_err.decode()))
                    else:
                        print(' '.join(assembly_command))
                        ret, std_out, std_err = run_notty_command(assembly_command)
                    if ret:
                        print("Assembly Failed Command:\n\t%s\nError Message:\n\t%s" % (assembly_command, std_err))
                
                elif compiler == CompileType.AR or compiler == CompileType.LD:
                    print("Link: ", file_name)
                else:
                    print("Unrecoganize: ", file_name)
                    
                self.finished_queue.put(work_node)
    
    
    def __update_work_graph(self, work_graph):
        
        while not self.finished_queue.empty():
            finished_node = self.finished_queue.get()
            successors = work_graph.successors(finished_node)
            
            if not successors:
                continue
            
            for successor in successors:
                degree_dict = work_graph.node.get(successor)
                if not degree_dict:
                    self.work_queue.put(successor)
                
                degree_dict[successor] -= 1
                if degree_dict[successor] == 0:
                    self.work_queue.put(successor)